﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Json;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using Microsoft.VirtualEarth.MapControl;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Silverlightaroo
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();

            //ResultMap.MouseClick += new EventHandler<MapMouseEventArgs>(c_MouseLeftButtonUp);

        }

        /// <summary>
        /// The search results
        /// </summary>
        protected List<SearchResult> searchResultsList;

        /// <summary>
        /// Start async request for JSON
        /// http://msdn.microsoft.com/en-us/library/cc197953(VS.95).aspx
        /// 
        /// Loading animation from 
        /// http://blog.buttonchrome.co.uk/post/Silverlight-Loadinge-Spin-Icon-in-XAML.aspx
        /// </summary>
        private void Search_Click(object sender, RoutedEventArgs e)
        {
            string host = Application.Current.Host.Source.Host;
            try
            {
                searchResultsList = new List<SearchResult>(); resultList.ItemsSource = searchResultsList; // blank out previous
                if (Application.Current.Host.Source.Port != 80)
                {
                    host = host + ":" + Application.Current.Host.Source.Port;
                }
                Uri serviceUri = new Uri("http://" + host + "/SearchJson.aspx?searchfor=" + query.Text,UriKind.Absolute);
                WebClient downloader = new WebClient();
                downloader.OpenReadCompleted += new OpenReadCompletedEventHandler(downloader_OpenReadCompleted);
                // loading animation thanks to http://blog.buttonchrome.co.uk/post/Silverlight-Loadinge-Spin-Icon-in-XAML.aspx
                downloader.OpenReadCompleted += (s, e1) => Spinner.Visibility = Visibility.Collapsed;
                Spinner.Visibility = Visibility.Visible;
                downloader.OpenReadAsync(serviceUri);
            }
            catch (Exception ex)
            {
                searchResultsList.Add(new SearchResult { name = "error on " + host, description = ex.Message, size = "-1", url = "#" });
                resultList.ItemsSource = searchResultsList;
            }
        }
        /// <summary>
        /// Filter the resultlist by 'images' or 'NOT images'
        /// </summary>
        List<SearchResult> Filter(List<SearchResult> list)
        {
            if (searchAllRadio.IsChecked.GetValueOrDefault())
            {
                return list;
            }
            else if (searchImageRadio.IsChecked.GetValueOrDefault())
            {
                var i = (from result in list
                         where result.url.EndsWith("jpg")
                         select result).ToList();
                return i;
            }
            else if (searchWebRadio.IsChecked.GetValueOrDefault())
            {
                var i = (from result in list
                         where !result.url.EndsWith("jpg")
                         select result).ToList();
                return i;
            }
            return list;
        }

        MapLayer PinMapLayer = new MapLayer(); // Layer on map to 'add' stuff to

        /// <summary>
        /// Receive JSON stream, parse into objects and bind to ListBox
        /// http://msdn.microsoft.com/en-us/library/cc197957(VS.95).aspx
        /// </summary>
        void downloader_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                using (Stream responseStream = e.Result)
                {
                    try
                    {
                        JsonArray resultStream = (JsonArray)JsonArray.Load(responseStream);
                        var results = from result in resultStream
                                      select result;
                        List<SearchResult> list = new List<SearchResult>();


                        if (!ResultMap.Children.Contains(PinMapLayer))
                        {
                            ResultMap.AddChild(PinMapLayer);  // added to map
                        }
                        else
                        {
                            PinMapLayer.Children.Clear();
                        }
                        Location min = new Location(), max = new Location(); // for map

                        int rowNum = 0, geoRowNum = 0;
                        foreach (JsonObject r in results)
                        {
                            var result = new SearchResult
                            {
                                name = "["+ ++rowNum+"] "+r["name"]
                                , description = r["description"]
                                , url = r["url"]
                                , size = r["size"]
                                , date = r["date"]
                                , tags = r["tags"]
                                , extension = r["extension"]
                                , gps = r["gps"]
                                , rank = r["rank"]
                            };
                            list.Add(result);
                            if (!String.IsNullOrEmpty(result.gps))
                            {
                                var l = new Location(
                                    Convert.ToDouble(result.gps.Split(',')[1])
                                    , Convert.ToDouble(result.gps.Split(',')[0])
                                    );
                                if (!LocationIsEmpty(l))
                                {
                                    geoRowNum++;
                                    
                                    //var tb = new TextBlock();
                                    //tb.Text = result.name;


                                    //tb.SetValue(MapLayer.MapPositionProperty, l);
                                    //ml.AddChild(tb);
                                    SetMinMax(l, ref min, ref max);
                                    // The pushpin image to add to the map.
                                    Image pin = new Image();
                                    pin.Source = new BitmapImage(new Uri("blue_pushpin.png", UriKind.RelativeOrAbsolute));
                                    // Defines a non-scaleable pushpin image
                                    pin.Stretch = Stretch.None;

                                    // thanks to http://pietschsoft.com/post/2009/03/Virtual-Earth-Silverlight-Basics-of-Adding-Polygons-and-Polylines-using-XAML-and-Code.aspx
                                    // Add Tooltip
                                    var tooltipObject = new StackPanel();
                                    var title = new TextBlock();
                                    title.FontWeight = FontWeights.Bold;
                                    title.Text = result.name;

                                    //var link = new HyperlinkButton();
                                    //link.NavigateUri = new Uri(result.url);
                                    //link.Content = title;
                                    //tooltipObject.Children.Add(link);

                                    tooltipObject.Children.Add(title);
                                    var description = new TextBlock();
                                    description.Text = result.description;
                                    tooltipObject.Children.Add(description);

                                    ToolTipService.SetToolTip(pin, tooltipObject);

                                    // Adds the pushpin to the map layer.
                                    PinMapLayer.AddChild(pin, l, PositionMethod.BottomCenter);

                                    //<Canvas Width="10" Height="10" Background="Blue" 
                                    // m:MapLayer.MapPosition="0,0" m:MapLayer.MapPositionMethod="Center"></Canvas>
                                    //var c = new Canvas();
                                    //c.Width = 10;
                                    //c.Height = 10;
                                    //c.Background = new System.Windows.Media.SolidColorBrush(Colors.Cyan);
                                    //c.SetValue(MapLayer.MapPositionProperty, l);
                                    //c.SetValue(MapLayer.MapPositionMethodProperty, PositionMethod.Center);
                                    ////c.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(c_MouseLeftButtonUp);
                                    //ml.AddChild(c);
                                }
                            }
                        }
                        if (geoRowNum > 0)
                        {
                            ResultMap.Visibility = Visibility.Visible;
                            ResultMap.SetView(GetMinMaxCenter(min, max), 12);
                        }
                        else
                        {
                            ResultMap.Visibility = Visibility.Collapsed;
                        }
                        searchResultsList = list;
                        resultList.ItemsSource = Filter(searchResultsList);
                    }
                    catch (Exception ex)
                    {
                        searchResultsList = new List<SearchResult>();
                        searchResultsList.Add(new SearchResult { name = "error", description = ex.Message, size = "", url = "#" });
                        resultList.ItemsSource = searchResultsList;
                    }
                    //HACK: couldn't get this working... not sure why
                    //DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(SearchResponse));
                    //SearchResponse sr = (SearchResponse)serializer.ReadObject(responseStream);
                    //resultList.DataContext = sr;
                }
            }
        }

        private bool LocationIsEmpty(Location l)
        {
            return (l.Latitude == 0 && l.Longitude == 0);
        }
        private void SetMinMax(Location l, ref Location min, ref Location max)
        {
            if (LocationIsEmpty(min)) min = l;
            if (LocationIsEmpty(max)) max = l;

            if (l.Latitude < min.Latitude) min.Latitude = l.Latitude;
            if (l.Longitude < min.Longitude) min.Longitude = l.Longitude;

            if (l.Latitude > max.Latitude) max.Latitude = l.Latitude;
            if (l.Longitude > max.Longitude) max.Longitude = l.Longitude;
        }
        private Location GetMinMaxCenter(Location min, Location max)
        {
            var center = new Location();
            center.Latitude = min.Latitude + (max.Latitude - min.Latitude) / 2;
            center.Longitude = min.Longitude+(max.Longitude - min.Longitude) / 2;
            return center;
        }
        

        /// <summary>
        /// Filter without re-querying
        /// </summary>
        private void searchRadio_Click(object sender, RoutedEventArgs e)
        {
            if (searchResultsList != null && searchResultsList.Count > 0)
            {
             //   var source = e.OriginalSource;
                resultList.ItemsSource = Filter(searchResultsList);
            }
        }
    }
    /// <summary>
    /// SearchResult class to display results - matches the Json 'class' sent from SearchJson.aspx
    /// </summary>
    public class SearchResult
    {
        public string name { get; set; }
        public string description { get; set; }
        public string url { get; set; }
        public string tags { get; set; }
        public string size { get; set; }
        public string date { get; set; }
        public string rank { get; set; }
        public string gps { get; set; }
        public string extension { get; set; }
        /// <summary>For display only</summary>
        public string summary { get { return String.Format("{0} - {1} - {2}", url, size, date); } }
        
        /// <summary>For display only - doesn't work though</summary>
        public TextBlock descriptionBlock { get {
            string xamlString = @"<TextBlock xmlns='http://schemas.microsoft.com/client/2007'
              xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>"; 

            string d = description.Replace("<b>", @"<Run FontWeight=""Bold"">");
            d = d.Replace("</b>", @"</Run>");

            d = xamlString + d + "</TextBlock>";

            TextBlock t = XamlReader.Load(d) as TextBlock;
            return t;
        } }
    }
}
